home *** CD-ROM | disk | FTP | other *** search
/ FishMarket 1.0 / FishMarket v1.0.iso / fishies / 301-325 / disk_319 / cnewssrc / cnews.src.lzh / relay / relaynews.c < prev    next >
C/C++ Source or Header  |  1989-07-31  |  13KB  |  492 lines

  1. /*
  2.  * relaynews - relay Usenet news (version C)
  3.  * See the file COPYRIGHT for the copyright notice.
  4.  *
  5.  * relaynews should be setuid-news, setgid-news.  You'll need to install
  6.  * setnewsids setuid-root if setuid(geteuid()) doesn't work on your
  7.  * machine (e.g. on V7 and possibly SystemIII).
  8.  *
  9.  * Written by Geoff Collyer, 15-20 November 1985 and revised periodically
  10.  * since.
  11.  *
  12.  * relaynews parses article headers, rejects articles by newsgroup &
  13.  * message-id, files articles, updates the active & history files,
  14.  * transmits articles, and honours (infrequent) control messages, which do
  15.  * all sorts of varied and rococo things.  Control messages are implemented
  16.  * by separate programs.  relaynews reads a "sys" file to control the
  17.  * transmission of articles but can function as a promiscuous leaf node
  18.  * without one.  See ARPA Internet RFC 1036 nee 850 for the whole story.
  19.  *
  20.  * A truly radical notion: people may over-ride via environment variables
  21.  * the compiled-in default directories so IHCC kludges are not needed and
  22.  * testing is possible (and encouraged) in alternate directories.  This
  23.  * does cause a loss of privilege, to avoid spoofing.
  24.  *
  25.  * The disused old ihave/sendme protocol is dead; it's been broken in
  26.  * B news for ages but no one has noticed because it's essentially
  27.  * useless on the uucp network, especially when batching news articles.
  28.  * It is also only semi-documented, very wasteful and kludgey in the extreme.
  29.  * It is unreasonable to expect an ill-documented protocol to be implemented
  30.  * in new software, particularly one as kludgey as this one.
  31.  * The new ihave sys flag ("I") for NNTP is implemented (trivially).
  32.  *
  33.  * Portability vs SystemV.  relaynews uses dbm(3) and makes no apologies
  34.  * for so doing.  Imitation UNIX (registered trademark of AT&T in the
  35.  * United States) brand operating systems that lack dbm are going to
  36.  * have to use my incredibly slow dbm simulation, or another.
  37.  */
  38.  
  39. #ifdef DEBUG
  40. #  define STAT(x)    {    fprintf(stderr,"%s: %d, ",__FUNC__,__LINE__);\
  41.                         fprintf x;\
  42.                         fflush(stderr); }
  43. #else
  44. #  define STAT(x)
  45. #endif
  46.  
  47. #include <stdio.h>
  48. #include <ctype.h>
  49. #include <signal.h>        /* to make locking safe */
  50. #ifndef AMIGA
  51. #  include <sys/types.h>
  52. #endif /* AMIGA */
  53.  
  54. #include "libc.h"
  55. #include "news.h"
  56. #include "config.h"
  57. #include "fgetmfs.h"
  58. #include "active.h"
  59. #include "caches.h"
  60. #include "cpu.h"
  61. #include "fileart.h"
  62. #include "headers.h"
  63. #include "history.h"
  64. #include "transmit.h"
  65.  
  66. /*
  67.  * setuid-root program to set ids to news/news & rexec rnews with
  68.  * NEWSPERMS in the environment to break loops.
  69.  */
  70. #ifndef SETNEWSIDS
  71. #define SETNEWSIDS "setnewsids"
  72. #endif
  73.  
  74. /* exports */
  75. char *progname;
  76. boolean okrefusal = YES;            /* okay to refuse articles? */
  77. char *exclude = NULL;                /* site to exclude, for erik */
  78. boolean histreject = NO;            /* keep history of rejects? */
  79.  
  80. /* internal */
  81. static boolean userealids = NO;
  82.  
  83. /* imports */
  84. extern int optind;            /* set by getopt */
  85. extern char *optarg;
  86. extern statust cpinsart();        /* from procart.c */
  87.  
  88. /* forwards */
  89. extern void prelude(), setids(), procopts(), redirectlogs(), logfile();
  90. extern void getwdandcd();
  91. extern statust procargs(), relnmprocess(), process(), unbatch();
  92. extern boolean batchln();
  93. FORWARD boolean debugon();
  94.  
  95. /*
  96.  * main - take setuid precautions, switch to "news" ids, ignore signals,
  97.  * handle options, lock news system, process files & unlock news system.
  98.  */
  99. int
  100. main(argc, argv)
  101. int argc;
  102. char *argv[];
  103. {
  104.     statust status = ST_OKAY;
  105.     int redirlogs = 0;        /* redirect n std output streams to logs */
  106.     char *origdir = NULL;        /* current directory at start */
  107.  
  108.     progname = argv[0];
  109. #ifdef CSRIMALLOC
  110.     mal_debug(0);        /* was 2; 3 is too slow */
  111.     mal_leaktrace(0);    /* was 1 */
  112. #endif
  113.     prelude(argv);        /* various precautions; switch to "news" */
  114.  
  115.     /*
  116.      * ignore signals (for locking). relaynews runs quickly,
  117.      * so don't worry.
  118.      */
  119. #ifdef SIGINT
  120.     (void) signal(SIGINT, (sigarg_t)SIG_IGN);
  121. #endif
  122. #ifdef SIGQUIT
  123.     (void) signal(SIGQUIT, (sigarg_t)SIG_IGN);
  124. #endif
  125. #ifdef SIGHUP
  126.     (void) signal(SIGHUP, (sigarg_t)SIG_IGN);
  127. #endif
  128. #ifdef SIGTERM
  129.     (void) signal(SIGTERM, (sigarg_t)SIG_IGN);
  130. #endif
  131.  
  132.     procopts(argc, argv, &redirlogs, &okrefusal);
  133.  
  134.     newslock();            /* done here due to dbm internal cacheing */
  135.     if (redirlogs > 0) {
  136.         redirectlogs(redirlogs); /* redirect std output streams to logs */
  137. #ifdef MANYERRORS
  138.         (void) putc('\n', stderr);    /* leave a blank line */
  139.         /* prints "Jun  5 12:34:56" */
  140.         timestamp(stderr, (time_t *)NULL);
  141.         (void) putc('\n', stderr);
  142. #endif
  143.     }
  144.  
  145.     getwdandcd(argc, argv, &origdir);
  146.     status |= procargs(argc, argv, &origdir);
  147.  
  148.     status |= synccaches();        /* being cautious: write & close caches */
  149.     (void) fflush(stdout);        /* log file */
  150.     (void) fflush(stderr);        /* errlog file */
  151.  
  152. #ifdef notdef
  153. #  ifdef CSRIMALLOC
  154.     mal_dumpleaktrace(fileno(stderr));
  155. #  endif
  156. #endif
  157.     newsunlock();
  158.     exit(status);
  159.     /* NOTREACHED */
  160. }
  161.  
  162. /*
  163.  * reset various environmental things for safety: umask, alarm,
  164.  * environment variables (PATH, IFS), standard file descriptors,
  165.  * user & group ids.
  166.  */
  167. void
  168. prelude(argv)                /* setuid daemon prelude */
  169. char **argv;
  170. {
  171.     register char *newpath;
  172.  
  173.     (void) umask(2);        /* undo silly umasks, ignore newsumask() */
  174.     (void) alarm(0);        /* cancel any pending alarm */
  175.     newpath = malloc(STRLEN("PATH=") + strlen(newspath()) + 1);
  176.     if (newpath == NULL)
  177.         exit(1);        /* no chatter until stdfdopen */
  178.     (void) strcpy(newpath, "PATH=");
  179.     (void) strcat(newpath, newspath());
  180. #ifndef AMIGA
  181.     if (putenv(newpath) || putenv("IFS= \t\n"))
  182.         exit(1);        /* no chatter until stdfdopen */
  183. #endif
  184.     closeall(1);            /* closes all but std descriptors */
  185.     stdfdopen();            /* ensure standard descriptors are open */
  186.     setids(argv);            /* change of real and effective ids */
  187. }
  188.  
  189. /*
  190.  * change real and effective ids to real ids if unprivileged() is called,
  191.  * else to effective ("news") ids.  ctlfile((char *)0) will trigger a call
  192.  * to unprivileged() if any environment variables override the default
  193.  * path names.  unprivileged() in turn sets userealids.
  194.  *
  195.  * If setuid(geteuid()) fails, try execing a small, setuid-root program
  196.  * to just do "getpwnam(), getgrnam() (with NEWSPERMS set), setgid(),
  197.  * setuid()," and exec this program again.  If NEWSPERMS is set,
  198.  * the failure is a fatal error (recursive loop).
  199.  * This program (relaynews) can be setuid-news.
  200.  */
  201.  
  202. void setids(argv)
  203. char **argv;
  204. {
  205.     int newsuid, newsgid;
  206.  
  207.     (void) ctlfile((char *)NULL);
  208.     if (userealids)
  209.         newsuid = getuid(), newsgid = getgid();
  210.     else
  211.         newsuid = geteuid(), newsgid = getegid();
  212.     if (setgid(newsgid) < 0 || setuid(newsuid) < 0) {
  213.         if (getenv("NEWSPERMS") != 0)
  214.             error("recursive loop setting ids", "");
  215.         execv(ctlfile(SETNEWSIDS), argv);
  216.         error("can't exec `%s' to set ids", ctlfile(SETNEWSIDS));
  217.         /* NOTREACHED */
  218.     }
  219.     /* we are now running as news, so you can all relax */
  220. }
  221.  
  222. /*
  223.  * parse options and set flags
  224.  */
  225.  
  226. void procopts(argc, argv, redirlogsp, okrefusalp)
  227. int argc;
  228. char **argv;
  229. int *redirlogsp;
  230. boolean *okrefusalp;
  231. {
  232.     int c, errflg = 0;
  233.  
  234.     while ((c = getopt(argc, argv, "d:inrsx:")) != EOF)
  235.         switch (c) {
  236.         case 'd':        /* -d debug-options; thanks, henry */
  237.             if (!debugon(optarg))
  238.                 errflg++;    /* debugon has complained */
  239.             break;
  240.         case 'i':        /* redirect stdout to log (inews) */
  241.             *redirlogsp = 1; /* just stdout */
  242.             break;
  243.         case 'n':        /* nntp mode: keep history of rejects */
  244.             histreject = YES;
  245.             break;
  246.         case 'r':        /* redirect std. ostreams to logs (rnews) */
  247.             *redirlogsp = 2; /* stdout & stderr */
  248.             break;
  249.         case 's':        /* dropping input is serious (inews) */
  250.             *okrefusalp = NO;
  251.             break;
  252.         case 'x':        /* -x site: don't send to site */
  253.             /* you're welcome, erik */
  254.             /* erik says he only needs one -x per inews */
  255.             if (exclude != NULL) {
  256.                 (void) fprintf(stderr,
  257.                     "%s: more than one -x site (%s)\n", progname, optarg);
  258.                 errflg++;
  259.             } else
  260.                 exclude = optarg;
  261.             break;
  262.         default:
  263.             errflg++;
  264.             break;
  265.         }
  266.     if (errflg) {
  267.         (void) fprintf(stderr,
  268.             "usage: %s [-inrs][-d fhlmt][-x site]\n", progname);
  269.         (void) fprintf(stderr,
  270.             "       -df ... file, -dh ... hdr, -dl ... lock,\n");
  271.         (void) fprintf(stderr,
  272.             "       -dm ... match, -dt ... trans\n");
  273.         exit(2);
  274.     }
  275. }
  276.  
  277. void unprivileged()        /* called if NEWSARTS, NEWSCTL or NEWSBIN present */
  278. {
  279.     userealids = YES;
  280. }
  281.  
  282. STATIC boolean
  283. debugon(dbopt)
  284. register char *dbopt;
  285. {
  286.     statust status = YES;
  287.  
  288.     for (; *dbopt != '\0'; dbopt++)
  289.         switch (*dbopt) {
  290.         case 'f':
  291.             filedebug(YES);
  292.             break;
  293.         case 'h':
  294.             hdrdebug(YES);
  295.             break;
  296.         case 'l':
  297.             lockdebug(YES);
  298.             break;
  299.         case 'm':
  300.             matchdebug(YES);
  301.             break;
  302.         case 't':
  303.             transdebug(YES);
  304.             break;
  305.         default:
  306.             status = NO;    /* unknown debugging option */
  307.             (void) fprintf(stderr, "%s: bad -d %c\n", progname, *dbopt);
  308.             break;
  309.         }
  310.     return status;
  311. }
  312.  
  313. /*
  314.  * Redirect stdout or stderr into log files at known locations.
  315.  */
  316.  
  317. void redirectlogs(count)
  318. int count;
  319. {
  320.     if (count > 0)
  321.         logfile(stdout, ctlfile("log"));
  322.     if (count > 1)
  323.         logfile(stderr, ctlfile("errlog"));
  324. }
  325.  
  326. void logfile(stream, name)            /* redirect stream into name */
  327. FILE *stream;
  328. char *name;
  329. {
  330.     if (freopen(name, "a", stream) == NULL)
  331.         errunlock("can't redirect standard stream to `%s'", name);
  332. }
  333.  
  334. /*
  335.  * if argv contains relative file name arguments, save current directory name
  336.  * in malloced memory, through origdirp.
  337.  * then change directory to the spool directory ($NEWSARTS).
  338.  */
  339.  
  340. void getwdandcd(argc, argv, origdirp)
  341. int argc;
  342. char **argv;
  343. char **origdirp;
  344. {
  345.     register int argind;
  346.     boolean needpwd = NO;
  347.     char dirtmp[MAXPATH];            /* much bigger than needed */
  348.  
  349.     for (argind = optind; argind < argc; argind++)
  350.         if (argv[argind][0] != FNDELIM)
  351.             needpwd = YES;
  352.  
  353.     *origdirp = "/???";            /* pessimism */
  354.     if (needpwd && getcwd(dirtmp, sizeof dirtmp) != 0)
  355.         *origdirp = dirtmp;
  356.     *origdirp = strsave(*origdirp);        /* save a smaller copy */
  357.     cd(fullartfile((char *)NULL));        /* move to spool directory */
  358. }
  359.  
  360. /*
  361.  * process files named as arguments (or implied)
  362.  */
  363.  
  364. statust procargs(argc, argv, origdirp)
  365. int argc;
  366. char **argv;
  367. char **origdirp;
  368. {
  369.     register statust status = ST_OKAY;
  370.  
  371.     if (optind == argc)
  372.         status |= process(stdin, "stdin");
  373.     else
  374.         for (; optind < argc; optind++)
  375.             status |= relnmprocess(argv[optind], *origdirp);
  376.     nnfree(origdirp);
  377.     return status;
  378. }
  379.  
  380. statust relnmprocess(name, origdir)        /* process a (relative) file name */
  381. char *name, *origdir;
  382. {
  383.     register statust status = ST_OKAY;
  384.     register FILE *in;
  385.     register char *fullname;
  386.  
  387.     fullname = nemalloc((unsigned)strlen(origdir) + STRLEN(SFNDELIM) +
  388.         strlen(name) + 1);
  389.     fullname[0] = '\0';
  390.  
  391.     if (name[0] != FNDELIM) {    /* relative path */
  392.         (void) strcat(fullname, origdir);
  393.         (void) strcat(fullname, SFNDELIM);
  394.     }
  395.     (void) strcat(fullname, name);
  396.  
  397.     in = fopenwclex(fullname, "r");
  398.     if (in != NULL) {
  399.         status |= process(in, fullname);
  400.         (void) nfclose(in);
  401.     }
  402.     free(fullname);
  403.     return status;
  404. }
  405.  
  406. /*
  407.  * process - process input file
  408.  * If it starts with '#', assume it's a batch and unravel it,
  409.  * else it's a single article, so just inject it.
  410.  */
  411.  
  412. statust process(in, inname)
  413. FILE *in;
  414. char *inname;
  415. {
  416.     register int c;
  417.  
  418.     if ((c = getc(in)) == EOF)
  419.         return ST_OKAY;         /* normal EOF */
  420.     (void) ungetc(c, in);
  421.     if (c == '#')
  422.         return unbatch(in, inname);
  423.     else
  424.         return cpinsart(in, inname, MAXLONG, NO);
  425. }
  426.  
  427. /*
  428.  * Unwind "in" and insert each article.
  429.  * For each article, call cpinsart to copy the article from "in" into
  430.  * a (temporary) file in the news spool directory and rename the temp file
  431.  * to the correct final name if it isn't right already.
  432.  *
  433.  * If the unbatcher gets out of sync with the input batch, the unbatcher
  434.  * will print and discard each input line until it gets back in sync.
  435.  */
  436.  
  437. statust unbatch(in, inname)
  438. register FILE *in;
  439. char *inname;
  440. {
  441.     register int c;
  442.     /* register */ char *line;
  443.     register statust status = ST_OKAY;
  444.     long charcnt;
  445.  
  446.     while (!(status&ST_DISKFULL) && (c = getc(in)) != EOF) {
  447.         (void) ungetc(c, in);
  448.         STAT((stderr, "read char '%c'\n", c));
  449.         while ((line = fgetms(in)) != NULL &&
  450.                     !batchln(line, &charcnt)) {        /* returns charcnt */
  451.             status |= ST_DROPPED;
  452.             (void) fprintf(stderr,
  453.                 "\n%s: unbatcher out of synch: ", progname);
  454.             (void) fputs(line, stderr);
  455.             free(line);
  456.         }
  457.         STAT((stderr, "count (%ld) line okay: %s", charcnt, line));
  458.         nnfree(&line);            /* free "#! rnews n" */
  459.         if (!feof(in))
  460.             status |= cpinsart(in, inname, charcnt, YES);
  461.     }
  462.     if (ferror(in))
  463.         errunlock("error reading `%s'", inname);
  464.     return status;
  465. }
  466.  
  467. /*
  468.  * Is line a batcher-produced line (#! rnews count)?
  469.  * If so, return the count through charcntp.
  470.  * This is slightly less convenient than sscanf, but a lot smaller.
  471.  */
  472.  
  473. boolean batchln(line, charcntp)
  474. register char *line;
  475. register long *charcntp;
  476. {
  477.     extern long atol();
  478.     register char *countp;
  479.     static char batchtext[] = "#! rnews ";
  480.  
  481.     countp = line + STRLEN(batchtext);
  482.     STAT((stderr, "line: %s\ncount: %s\n", line, countp));
  483.     if (STREQN(line, batchtext, STRLEN(batchtext)) &&
  484.             isascii(*countp) && isdigit(*countp)) {
  485.         *charcntp = atol(countp);
  486.         return YES;
  487.     } else {
  488.         *charcntp = 0;
  489.         return NO;
  490.     }
  491. }
  492.